package org.springframework.boot.context.properties;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.bind.PropertySourcesPlaceholdersResolver;
import org.springframework.boot.context.properties.source.IterableConfigurationPropertySource;
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.util.ReflectionUtils;

import java.util.Arrays;
import java.util.Map;
import java.util.Properties;

public class YamlPropertiesBinder {

    public static <T> BindResult<T> bind(String prefix, Class<T> clazz, Properties properties) {

        prepareProperties(prefix, clazz, properties);

        return getBinder(properties).bind(prefix, clazz);
    }

    private static <T> void prepareProperties(String prefix, Class<T> clazz, Properties properties) {
        ReflectionUtils.doWithFields(clazz, (field) -> {
            Value value = field.getAnnotation(Value.class);
            if (null != value) {
                String valStr = value.value();
                valStr = valStr.replace("${", "");
                valStr = valStr.replace("}", "");
                valStr = prefix + "." + valStr;
                Object o = properties.get(valStr);
                if (o != null) {
                    properties.put(prefix + "." + field.getName(), properties.get(valStr));
                }
            }
        });
    }

    public static <T> T bindOrCreate(String prefix, Class<T> clazz, Properties properties) {
        prepareProperties(prefix, clazz, properties);
        return getBinder(properties).bindOrCreate(prefix, clazz);
    }

    private static Binder getBinder(Properties properties) {

        Properties dataProperties = new Properties();
        for (Map.Entry<Object, Object> objectObjectEntry : properties.entrySet()) {
            Object key = objectObjectEntry.getKey();
//            dataProperties.put(DataObjectPropertyName.toDashedForm(key.toString()), objectObjectEntry.getValue());
            dataProperties.put(key, objectObjectEntry.getValue());
        }

        MutablePropertySources mutablePropertySources = new MutablePropertySources();
        PropertySource<?> placePropertySource = new PropertySource<Object>("source", properties) {
            @Override
            public Object getProperty(String name) {
                return properties.get(name);
            }
        };
        mutablePropertySources.addFirst(placePropertySource);
        PropertySourcesPlaceholdersResolver propertySourcesPlaceholdersResolver =
                new PropertySourcesPlaceholdersResolver(mutablePropertySources);

        IterableConfigurationPropertySource propertySource = new MapConfigurationPropertySource(dataProperties);
//        Binder binder = new Binder(Arrays.asList(propertySource), propertySourcesPlaceholdersResolver);

//        binder.
        return new Binder(Arrays.asList(propertySource), propertySourcesPlaceholdersResolver);
    }
}
